代码多副本功能

在NUMA架构(尤其是ARM实例)中,不同NUMA节点具有各自的本地内存,当一个NUMA节点上的程序或进程需要访问其他NUMA节点的代码段时,就会引入额外的延迟和性能开销。通过代码多副本功能,可以将远程节点的代码段复制到本地节点,避免了跨节点访问,从而解决NUMA架构中因跨节点访问带来的性能延迟问题。

背景信息

  • 什么是代码段跨节点访问?对系统有什么影响?

    • 什么是代码段跨节点访问?

      非统一内存访问NUMA (Non-Uniform Memory Access) 是一种计算机架构设计,用于处理多处理器系统中的内存访问延迟和带宽不均匀的问题。不同的处理器核心和内存子系统整体被称为节点(node),每个节点都有自己的本地内存。当一个进程在NUMA架构中运行时,可能由于内存不足或负载平衡等原因,操作系统会将内存分配在其他节点上。当应用程序或进程需要访问的代码段存储在另一个NUMA节点上时,就需要通过跨节点访问的方式来获取代码段中的指令。

    • 对系统有什么影响?

      跨节点访问代码段可能会产生一些性能问题,例如增加访问延迟、降低执行效率和增加内存带宽的占用等。这是由于跨节点访问需要通过总线或互联网络传输数据,并在远程节点上进行内存访问,这些操作通常比本地内存访问更慢,所以要尽可能避免应用跨节点访问。

  • 代码多副本功能解决代码段跨节点访问的技术原理

    代码多副本的核心作用是当发现应用程序或进程跨节点访问代码段时,在当前节点上创建待访问代码段的副本,这样可以在本地节点上访问代码段,而无需进行跨节点访问,从而减少跨节点访问带来的延迟和开销。如下图所示:

    1. node 1上的Process 1进程跨节点访问node 0上的libc.so代码段,或者node 0上的Process 0进程跨节点访问node 1上的test代码段。

    2. 此时通过代码多副本功能,会在node 1上创建libc.so副本,在node 0上创建test副本。

    3. 那么Process 1进程实际访问的是libc.so副本,Process 0进程实际访问的是test副本,从而消除跨节点访存。

    说明

    内核以页(4 KB大小)为粒度管理内存,一个程序的代码段可能放在一个或多个内存页中。一个程序的代码段第一次被从硬盘读入到内存,加入到Page Cache(文件缓存)中,所在的内存页叫主页。如果内核发现某个程序访问了远端节点上的代码段,就会把这些代码段以页为粒度在本地节点上拷贝一份,那么本地代码段的页就叫副页。

    image

使用限制

仅以下实例规格和镜像支持代码多副本功能。

  • 实例规格:弹性裸金属服务器规格族群。更多信息,请参见弹性裸金属服务器

  • 镜像:内核版本是5.10.112-11及以上的Alibaba Cloud Linux 3镜像。

    说明

    您可以通过uname -r命令查询镜像的内核版本。

配置开关

代码多副本提供了全局开关和memcg开关,只有当两个开关同时打开时,内核系统内的进程才能使用代码多副本功能。

配置开关

说明

/sys/kernel/mm/duptext/enabled

全局开关用于设置当前内核系统是否启用代码多副本功能。取值范围:0~1,默认值为0。

  • 取值为1,表示允许启用代码多副本功能。

  • 取值为0,表示禁用或关闭代码多副本功能。

    说明

    关闭代码多副本功能时,内核会自动清除整机上的所有副页。

/sys/fs/cgroup/memory/<memcg目录名称>/memory.allow_duptext

在全局开关打开的前提下,memcg开关用于设置各memcg中的进程是否启用代码多副本功能。取值范围:0~1,默认值为0。

  • 取值为1,表示当前memcg中的进程启用代码多副本功能。

  • 取值为0,表示当前memcg中的进程不启用代码多副本功能。

说明

除了上述特性开关之外,您还可以通过以下方式查看副页的统计数据。

  • 通过/proc/vmstat中的nr_duptext字段或者/proc/meminfo中的DupText字段查看整机的副页统计数据。

    • nr_duptext表示在内核中标记为duptext的副页数量。

    • DupText表示使用了多少内存来存储duptext相关的数据,单位为KB。通常1个内存页的大小是4 KB。

  • 通过/proc/pid/smaps文件可以查看对应进程的副页统计数据。

使用代码多副本功能

本示例通过在一台具有两个NUMA节点的ECS实例上编译并执行一个测试程序test.c为例,演示代码多副本功能的用法。

  1. 远程登录ECS实例。

    具体操作,请参见通过密码或密钥认证登录Linux实例

  2. (可选)运行以下命令,查看ECS实例的NUMA节点信息。

    numactl -H
    说明

    如果您没有安装numactl,可执行sudo yum install numactl命令进行安装。

    如下图所示,表示该实例有2个NUMA节点(node 0和node 1)。

    image.png
  3. 运行以下命令,编译测试程序并生成可执行文件。

    本示例假设在node 1上编译test.c的源代码文件,并在node 1上生成test文件的Page Cache。

    numactl -N 1 -m 1 gcc test.c -o test
  4. 运行以下命令,打开代码多副本功能的全局开关。

    sudo sh -c 'echo 1 > /sys/kernel/mm/duptext/enabled'
  5. 运行以下命令,创建一个memcg目录并打开memcg的多副本功能开关。

    sudo mkdir /sys/fs/cgroup/memory/test
    sudo sh -c 'echo 1 > /sys/fs/cgroup/memory/test/memory.allow_duptext'
  6. 运行以下命令,通过代码多副本功能避免跨节点访问。

    本示例假设使用cgexec和numactl来运行可执行文件test,同时将进程绑定到node 0上。此时会在node 0上创建test相关代码段的副本,即直接访问的是node 0上的副本,避免了跨节点访问。

    sudo cgexec -g "memory:test" numactl -N 0 -m 0 ./test
    说明

    如果您没有安装cgexec,可执行sudo yum install -y libcgroup-tools命令进行安装。

  7. 运行以下命令,查看test程序的副页统计数据。

    sudo cat /proc/$(pidof test)/smaps

    如下图所示,展示了test的副页统计信息,表示在node 0上已经成功生成了test的副本。

    image.png
    说明

    您也可以分别运行以下命令,查看整机的副页统计数据。

    cat /proc/vmstat | grep -i duptext
    cat /proc/meminfo | grep -i duptext

关闭代码多副本功能

后续您可以根据需要关闭代码多副本功能。关闭多副本功能的同时,内核会自动清除整机上的所有副页。

  1. 远程登录ECS实例。

    具体操作,请参见通过密码或密钥认证登录Linux实例

  2. 运行以下命令,关闭代码多副本功能。

    sudo sh -c 'echo 0 > /sys/kernel/mm/duptext/enabled'
  3. 运行以下命令,验证是否关闭代码多副本功能。

    cat /proc/vmstat | grep -i duptext
    cat /proc/meminfo | grep -i duptext

    如下图所示,整机上的所有副页已经被清除,表示已经关闭代码多副本功能。

    image.png